#include <QMutexLocker>
#include "interprete.h"
#include "entrenador.h"

Interprete::Interprete()
{
    numeroNotaActual = 0;
    numeroCompasActual = 0;
    numeroCompasAnteriorMetronomo = 0;
    numeroCompasInicioPatron = 0;
    cambioDePatron = false;
    consignaTempo = 0;
}

Interprete::~Interprete()
{

}

void Interprete::setTempo(int tempo)
{
    if (!activo()) {
        consignaTempo = tempo;
        metronomo.setTempo(tempo);
    } else {
        consignaTempo = tempo;
    }
}

void Interprete::setEjercicio(Ejercicio ejercicio)
{
    this->ejercicio = ejercicio;

    asignarCompasActual(0);
}

void Interprete::setPatron(const Patron& patron)
{
    if (patron.getNombre() == this->patron.getNombre()) {
        return;
    }
	this->patron = patron;
    // TODO: ver que pasa con tiempos en amalgama
    Compas compas = patron.getCompas(0);

    metronomo.setPulsosPorCompas(compas.getNumeroPulsos());

    cambioDePatron = true;
}

void Interprete::inicio()
{	
    ejercicio.iniciar();

    primeraNota();

    numeroCompasAnteriorMetronomo = 0;
    metronomo.arrancar();
}

void Interprete::proceso()
{	
    int numeroCompasMetronomo = metronomo.getNumeroCompasActual();
    if (numeroCompasAnteriorMetronomo != numeroCompasMetronomo && consignaTempo != metronomo.getTempo()) {
        metronomo.setTempo(consignaTempo);
    }
    numeroCompasAnteriorMetronomo = numeroCompasMetronomo;

    //if (!notaActual.esValida()) {
    //    siguienteNota();
    //    return;
    //}

    while (procesarNota() && !terminando()) {
        siguienteNota();
    }

    metronomo.comprobarSonido();
}

void Interprete::fin()
{
    metronomo.parar();
}

quint64 Interprete::getMilisegundoInicioPatron()
{        
    quint64 msInicioPatron = metronomo.getMilisegundoNota(NotaCompas("", 0, 0), numeroCompasInicioPatron);
    return msInicioPatron;
}

#define MARGEN_ERROR_MS 20

bool Interprete::procesarNota()
{
    if (!notaActual.esValida()) {
        siguienteNota();
        if (!notaActual.esValida()) {
            return false;
        }
    }

    double diferenciaMilisegundos = metronomo.distanciaNota(notaActual, numeroCompasActual);

    if (diferenciaMilisegundos > MARGEN_ERROR_MS) {
        // aun no le toca
        return false;
    }
    if (diferenciaMilisegundos < -MARGEN_ERROR_MS) {
        // se ha pasado su momento
        qDebug() << "Se paso su momento";
        return true;
    }


    QString nombreNota = notaActual.getNombreNota();
    Entrenador::getInstancia()->marcarNotaEntrenamiento(nombreNota);	
    return true;
}

Compas Interprete::getCompasActual()
{
    QMutexLocker locker(&mutex);
    return compasActual;
}

double Interprete::getProporcionCompletadaDelPatron() 
{    
    int numeroCompases = patron.getNumeroCompases();
    if (numeroCompases == 0) {
        return 0;
    }
    double proporcionCompletadoCompas = metronomo.getProporcionCompletadoCompas();
    int numeroCompasRelativoAlPatron = metronomo.getNumeroCompasActual() % numeroCompases;
    return (numeroCompasRelativoAlPatron + proporcionCompletadoCompas) / numeroCompases;
}

void Interprete::primeraNota()
{   
    asignarCompasActual(0);

    if (compasActual.getNumeroNotas() == 0) {
        notaActual = NotaCompas();
        return;
    }

    numeroNotaActual = 0;
    notaActual = compasActual.getNota(0);

    
}

#define MS_ANTCIPACION_CAMBIO_COMPAS 10

/**
 * Elije la siguiente nota que se ha de ejecutar.
 */
void Interprete::siguienteNota()
{    
    if (patron.getNumeroCompases() == 0) {
        return;
    }

    int numeroCompasMetronomo = metronomo.getNumeroCompasActual();

    if (numeroCompasActual < numeroCompasMetronomo) {
        asignarCompasActual(numeroCompasMetronomo);
    } else if (numeroCompasActual == numeroCompasMetronomo) {
        double msHastaProximoCompas = metronomo.distanciaNota(NotaCompas("", 0, 0), numeroCompasActual + 1);
        //if (msHastaProximoCompas < MS_ANTCIPACION_CAMBIO_COMPAS) {
        //    asignarCompasActual(numeroCompasMetronomo + 1);
        //}
    } else {
        return;
    }

    if (numeroNotaActual >= compasActual.getNumeroNotas()) {
        // Ya no quedan mas notas del compas
        notaActual = NotaCompas();
        return;
    }

	notaActual = compasActual.getNota(numeroNotaActual++);
}

void Interprete::asignarCompasActual(int numeroCompas)
{
    QMutexLocker locker(&mutex);

    if (numeroCompas == 0 || numeroCompas - numeroCompasInicioPatron >= patron.getNumeroCompases()) {
        if (numeroCompas == 0) {
            ejercicio.iniciar();
        }
        if (ejercicio.haTerminado()) {
            desactivar();
            return;
        }

        
        patron = ejercicio.getSiguientePatron();
        numeroCompasInicioPatron = numeroCompas;

        cambioDePatron = true;
    }

    if (!patron.esValido()) {
        desactivar();
        return;
    }

    //qDebug() << "Compas actual: " << numeroCompas << " del patron " << patron.getNombre();
    numeroCompasActual = numeroCompas;
    numeroNotaActual = 0;
    compasActual = patron.getCompas(numeroCompasActual);
}

bool Interprete::haCambiadoPatron()
{
    if (cambioDePatron) {
        cambioDePatron = false;
        return true;
    } else  {
        return false;
    }
}
